package org.stayfool.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

import org.junit.Test;

public class NOISocketClient {

	private Selector s;

	private AtomicBoolean started = new AtomicBoolean(false);

	private ExecutorService es = Executors.newFixedThreadPool(8);

	public static void main(String[] arg) throws Exception {
		NOISocketClient c = new NOISocketClient();
		c.start();
	}

	@Test
	public void start() throws Exception {

		receive();

		s = Selector.open();
		SocketChannel cc = SocketChannel.open();
		cc.socket().connect(new InetSocketAddress("127.0.0.1", 9999));

		while (!cc.finishConnect()) {
			System.out.println("connecting......");
			Thread.sleep(1000);
		}

		started.set(true);
		System.out.println("client started : " + LocalDateTime.now().toString());

		cc.configureBlocking(false);
		cc.register(s, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);

		while (true) {
			Thread.sleep(100000000);
		}
	}

	@Test
	public void receive() {

		es.execute(() -> {
			// wait for server start
			while (!started.get()) {
				System.out.println("wait for client start");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			// receive
			while (true) {
				System.out.println("wait for data comming ");
				int readyChannels = 0;
				try {
					readyChannels = s.select();
				} catch (IOException e) {
					e.printStackTrace();
				}
				if (readyChannels == 0) {

					continue;
				}
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				Set<SelectionKey> selectedKeys = s.selectedKeys();
				Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
				while (keyIterator.hasNext()) {
					SelectionKey key = keyIterator.next();
					if (key.isAcceptable()) {
						// a connection was accepted by a ServerSocketChannel.
						System.out.println(" accept ");
					} else if (key.isConnectable()) {
						// a connection was established with a remote server.
						System.out.println(" connected ");
					} else if (key.isReadable()) {
						// a channel is ready for reading
						System.out.println(" read ");
						ByteBuffer wel = ByteBuffer.allocate(128);
						SocketChannel c = (SocketChannel) key.channel();
						try {
							c.read(wel);
							wel.flip();
							byte[] bytes = new byte[wel.limit()];
							wel.get(bytes);
							System.out.println(new String(bytes));
						} catch (IOException e) {
							e.printStackTrace();
						}

						try {
							c.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					} else if (key.isWritable()) {
						// a channel is ready for writing
						System.out.println(" write ");

						ByteBuffer wel = ByteBuffer.allocate(128);
						wel.put("I'm comming ......".getBytes());
						wel.flip();
						SocketChannel c = (SocketChannel) key.channel();

						try {
							c.write(wel);
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					keyIterator.remove();
				}
			}

		});
	}
}
